Objetivo: dada uma árvore de execução (σ, ⨝, π) e as estatísticas do catálogo, achar o custo mínimo total em acessos a bloco, justificando a escolha de cada operação pelo custo de todas as alternativas.
🧭 Comece aqui
A ideia em uma frase. O banco consegue responder a mesma consulta de vários jeitos: ler a tabela inteira, usar um índice, juntar duas tabelas de formas diferentes. A parte lenta é ler do disco, então o custo de cada jeito é medido em quantos blocos ele lê. O banco calcula o custo de todos os jeitos e escolhe o mais barato, e a prova pede que você faça a mesma conta.
Uma analogia. Você precisa achar as fichas dos alunos com nota 10 numa pilha com centenas de fichas. Jeito 1: olhar todas, o que sempre funciona. Jeito 2: usar uma lista separada, organizada por nota, que diz onde está cada ficha (um índice). O jeito 2 compensa se forem poucos alunos. Se metade da turma tirou 10, você acaba abrindo quase todas as fichas de qualquer jeito, e ainda pagou para ler a lista. Toda a matéria é essa comparação, com números.
O que a questão pede. Uma árvore com as operações da consulta. Para cada operação: liste todos os métodos possíveis, calcule o custo de cada um (ou diga por que não se aplica), escolha o menor e, no fim, some.
Palavras que vão aparecer:
Bloco: a página do arquivo que o disco lê de uma vez. Custo = número de blocos lidos.
Seleção (σ): filtra linhas (o WHERE).
Junção (⨝): combina linhas de duas tabelas (o JOIN).
Projeção (π): escolhe colunas (o SELECT colunas).
Seletividade: que fração das linhas passa no filtro. 20% = 200 de 1 000 linhas.
Índice: a "lista organizada" da analogia (ver "Banco de Dados: Indexação e Árvore B+").
Buffer: quantos blocos cabem na memória ao mesmo tempo durante uma junção ou ordenação.
Externa / interna: numa junção por laços, a tabela do laço de fora e a do laço de dentro.
Onde este arquivo se encaixa. É o 3º de 3. Ele usa o número de blocos de cada tabela ("Banco de Dados: Organização de Arquivos") e os níveis e folhas dos índices ("Banco de Dados: Indexação e Árvore B+").
Se você está perdido, leia nesta ordem: este "Comece aqui", depois o Exemplo resolvido (seção 11) acompanhando a Receita (seção 10). As seções 3 a 9 explicam cada conta do exemplo: volte a elas quando um número não fizer sentido.
⚙️ 1. Como o SGBD processa uma consulta
flowchart LR
A["SQL"] --> B["Interpretador<br/>léxico, sintático, semântico<br/>(consulta o catálogo)"]
B --> C["Árvore de consulta<br/>canônica"]
C --> D["Gerador de código<br/>(escolhe os algoritmos)"]
D --> E["Executor"] --> F["Resultado"]
Árvore de consulta: as folhas são as tabelas e os nós internos são as operações (σ, π, ⨝; ver o cheat sheet "Álgebra Relacional"). A execução vai de baixo para cima: cada operação recebe uma relação e devolve outra.
Canônica: a tradução direta do SQL como foi escrito, uma expressão para o bloco SELECT-FROM-WHERE-GROUP BY-HAVING.
Gerador de código: para cada operação, decide quais algoritmos podem ser usados, considerando o que existe (índices, ordenação) e os predicados. É aqui que o custo é estimado.
Materialização
Pipelining
Resultado intermediário
Gravado em disco
Passa por um buffer em memória direto para o operador de cima
Custo
+ escrita e releitura
Nenhum acesso extra
Quando
Operando não cabe na memória
Padrão. Operadores por tupla (σ, π) fluem; operadores que precisam da tabela inteira (ordenação) bloqueiam o pipeline.
O pipelining usa iteradores: Open() prepara a operação, GetNext() devolve a próxima tupla (ou NotFound) e Close() libera os buffers. Consequência na prova: a projeção no topo da árvore custa 0 com pipelining.
📖 2. Notação
Não decore esta tabela: cada símbolo aparece explicado na conta em que é usado. Quatro deles aparecem em quase toda conta:
b = blocos da tabela;
r = linhas;
s = linhas que passam no filtro;
x = níveis do índice.
A tabela completa abaixo é de consulta. Os valores ao lado são os do exemplo resolvido (seção 11).
Símbolo
Significado
Exemplo
r
Número de tuplas da tabela
empregado: 1 000
R
Tamanho de uma tupla em bytes: soma de todas as colunas
⚠️ Some todas as colunas, inclusive as que a consulta não usa. Esquecer o endereco (200 B) dá R=112 e um b errado, e o erro se propaga por todo o resto.
🎯 4. Passo 2: seletividade
A seletividade é a fração das tuplas que satisfaz a condição. Se 20% de 1 000 tuplas passam, a seleção devolve s=200 tuplas. Quanto menor, mais um índice compensa: o índice secundário paga cerca de 1 acesso por tupla encontrada. Com 200 de 1 000 (20%) ele ainda ganha da leitura da tabela inteira (207 < 334). Com 350 de 1 000 (35%), já perde (362 > 334).
Condição
Estimativa
Exemplo
Igualdade A = v
s=r/d (supõe os valores espalhados por igual)
salario = 4000: 1000/5 = 200 (20%)
Igualdade em chave (único)
s=1
id = 7
Faixa, com histograma
Soma das faixas cobertas
dt_nasc ≥ 1980: 200 + 150 = 350 (35%)
A AND B
sA∧B=r⋅rsA⋅rsB
1000 × 0,20 × 0,35 = 70 (7%)
A OR B
sA∨B=sA+sB−sA∧B
200 + 350 − 70 = 480 (48%)
Histograma: uma tabela do catálogo com a contagem de tuplas por faixa de valores. Em vez de supor tudo uniforme, você soma as faixas que a condição cobre. Se a condição corta uma faixa no meio, entra a parte proporcional (supondo uniforme dentro da faixa). Exemplo: dt_nasc ≥ 1985 pega metade da faixa 1980–1990, ou seja 200 × 5/10 + 150 = 250.
Independência: supõe que uma condição não diz nada sobre a outra (saber o salário não ajuda a prever a data de nascimento). Então as frações se multiplicam: 20% de 35% = 7%. Use sempre que o enunciado não disser outra coisa.
🔍 5. Passo 3: algoritmos de seleção
Método
Custo (acessos)
Quando vale
Exemplo
Busca sequencial
b
Sempre
334
Busca binária
⌈log2b⌉+⌈s/Bfr⌉−1
Arquivo ordenado pelo atributo
Não se aplica
Índice primário / clustering
x+⌈s/Bfr⌉
Arquivo ordenado pelo atributo e índice nele
Não se aplica
Índice secundário (B+)
x+⌈bleaf⋅s/r⌉−1+s
Índice num atributo que não ordena o arquivo
—
Conjuntiva, índice simples
Custo do índice de uma condição, com o s dela; a outra é testada em memória
Csec=desce ateˊ a primeira folhax+folhas com as s entradas⌈bleaf⋅rs⌉primeira folha jaˊ contada−1+1 bloco por tuplas
⌈bleaf⋅s/r⌉: as s entradas ficam juntas nas folhas, que são lidas em sequência pela lista encadeada. Com 30 folhas e 20% das tuplas, lê-se 30×0,2=6 folhas.
+s: no pior caso, cada tupla encontrada está num bloco de dados diferente.
c∗ é o custo do índice sem o +s:c∗=x+⌈bleaf⋅s/r⌉−1. Nos índices múltiplos, cada índice entrega só uma lista de rowIds. As listas são cruzadas em memória (interseção no AND, união no OR), e só as tuplas que sobram são buscadas no arquivo. No exemplo: csal∗=2+6−1=7 e cdt∗=2+11−1=12.
📦 6. Passo 4: tamanho do resultado intermediário
bsel=⌈Bfrs⌉=⌈370⌉=24 blocos
Use a mesma Bfr da tabela, porque a tupla não mudou de tamanho.
O resultado intermediário não tem índice e não está ordenado. Isso elimina opções na junção.
Índice no atributo de junção da relação interna, que precisa ser tabela base
índice em id_gerente: 24 + 70 × 3 = 234; departamento externa: não se aplica
Sort-merge
bR+bS + ordenações
Ordena as duas pelo atributo de junção (a ordenação some se já estiverem ordenadas)
144 + 5 + (24 + 5) = 178
Hash
2bR+bS + nested loops de cada bucket
R é a relação particionada
R = seleção: 53 + buckets; R = departamento: 34 + buckets ⚠️
Nested loops: a externa (outer) é lida em pedaços de bbuffer−2 blocos, e para cada pedaço a interna (inner) é lida inteira. Os 2 blocos que sobram são um para a interna e um para a saída. Com buffer de 5: pedaços de 3 blocos.
Index-based: para cada tupla da externa, faz-se uma busca no índice da interna. Aqui s=r/d=50/50=1 e c=2+⌈5×1/50⌉−1+1=3.
Ordenação: se a tabela cabe no buffer (b≤bbuffer), a ordenação é em memória e custa b. Senão, é externa:
Seleção (24 blocos, buffer 5): nr=5 runs; gm=4; ⌈log45⌉=2 passadas de merge (5 runs → 2 → 1); 2×(24+48)=144. O departamento (5 blocos) cabe no buffer e custa 5. O merge lê as duas: 24 + 5.
Como ler o log:⌈loggmnr⌉ é quantas passadas de merge são necessárias, juntando gm runs por vez, até sobrar uma só. Cada passada lê e grava a tabela inteira (o fator 2).
⚠️ Hash em aberto: o termo "nested loops de cada bucket" depende de quantos buckets há e do tamanho de cada um, e o enunciado não dá isso. Com R = departamento, o hash só ganharia se os buckets custassem menos de 19 (34 + 19 = 53). Adote o menor custo fechado (53) e escreva a suposição.
Tamanho do resultado da junção:r⋈=rR⋅rS⋅js, com js=1/max(dA,dB). No exemplo: 70×50/1000=3,5 → 4 tuplas de 312 + 96 = 408 B, Bfr=2 → 2 blocos.
📤 8. Passo 6: projeção e agregação
Projeção sem DISTINCT (bag): C=b da entrada. Com pipelining, ela é aplicada nas tuplas que saem da junção e não soma nada. Se for materializada, conte o b do resultado da junção (2 blocos no exemplo).
Agregação (com ou sem GROUP BY): C=b.
Projeção com DISTINCT: eliminar duplicatas exige ordenar (ou usar hash). Some o custo da ordenação.
🔢 9. Arredondamentos
Grandeza
Arredonda
Exemplo
Bfr
Para baixo
⌊1024/312⌋=3
b, bsel
Para cima
⌈70/3⌉=24
log (passadas, níveis)
Para cima
⌈log45⌉=2
Fração de bloco (bleaf⋅s/r, pedaços do nested loops)
Para cima, no mínimo 1
⌈30×0,35⌉=11; ⌈5/50⌉=1
Número de tuplas fracionário
Para cima (diga)
3,5 → 4
✅ 10. Receita na prova
R, Bfr e b de cada tabela, somando todas as colunas.
s de cada condição e da combinação (AND por independência, OR por inclusão-exclusão).
Seleção: uma tabela com todos os métodos, cada um com custo ou "não se aplica (motivo)". Escolha o menor e compare explicitamente com a busca sequencial.
b do resultado da seleção.
Junção:todos os métodos, nas duas ordens, com "não se aplica (motivo)" onde couber. Escolha o menor.
Projeção/agregação: diga se usou pipelining.
Total = soma dos mínimos, em acessos a bloco.
Escreva as suposições (hash, pipelining, arredondamentos).
📝 11. Exemplo resolvido completo
Enunciado: apresente o custo mínimo total da árvore de execução abaixo, justificando o custo mínimo de cada operação pelos custos das demais opções. Buffer para junção e ordenação: 5 blocos. Página: 1 024 B.